package br.edu.unifei.mestrado.db.graph;

import java.util.Iterator;

import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.NotFoundException;
import org.neo4j.graphdb.Relationship;

import br.edu.unifei.mestrado.commons.graph.GraphProperties;
import br.edu.unifei.mestrado.commons.graph.NodeWrapper;
import br.edu.unifei.mestrado.commons.graph.RelationshipWrapper;
import br.edu.unifei.mestrado.commons.partition.AbstractPartition;

public class NodeDB implements NodeWrapper {

	private Node node;

	public NodeDB(Node node) {
		this.node = node;
	}

	@Override
	public int getD() {
		try {
			Integer d = (Integer) node.getProperty(GraphProperties.D);
			assert d != null : "Propriedade " + GraphProperties.D + " não encontrada para Node: "
					+ node.getProperty(GraphProperties.D);
			return d;
		} catch (NotFoundException e) {
			return 0;
		}
	}

	@Override
	public long getId() {
		Long id = (Long) node.getProperty(GraphProperties.ID);
		assert id != null : "Propriedade " + GraphProperties.ID + " não encontrada para Node: "
				+ node.getProperty(GraphProperties.ID);

		return id;
	}

	@Override
	public int getPartition() {
		Integer partition = (Integer) node.getProperty(GraphProperties.PARTITION);

		// UTIL: aqui pode aceitar partição -1, indicando que o nó ainda não foi escolhido
		if (partition == null || partition < AbstractPartition.NO_PARTITION || partition > AbstractPartition.PART_N) {
			throw new RuntimeException("Partição inválida: " + partition + " para nó: " + getId());
		}

		assert partition != null : "Propriedade " + GraphProperties.PARTITION + " não encontrada para Node: "
				+ node.getProperty(GraphProperties.ID);

		return partition;
	}

	@Override
	public Iterable<RelationshipWrapper> getRelationships() {
		return new Iterable<RelationshipWrapper>() {

			@Override
			public Iterator<RelationshipWrapper> iterator() {
				return new Iterator<RelationshipWrapper>() {
					private Iterator<Relationship> edges = node.getRelationships().iterator();

					@Override
					public RelationshipWrapper next() {
						Relationship edge = edges.next();
						RelationshipWrapper rel = new RelationshipDB(edge);
						return rel;
					}

					@Override
					public boolean hasNext() {
						return edges.hasNext();
					}

					@Override
					public void remove() {
					}
				};
			}
		};

	}

	@Override
	public int getWeight() {
		Integer peso = (Integer) node.getProperty(GraphProperties.PESO);
		if (peso == null) {
			throw new UnsupportedOperationException("Propriedade " + GraphProperties.PESO
					+ " não encontrada para Node: " + node.getProperty(GraphProperties.ID));
		}
		return peso;
	}

	@Override
	public boolean hasProperty(String key) {
		return node.hasProperty(key);
	}

	@Override
	public boolean isLocked() {
		Boolean locked = (Boolean) node.getProperty(GraphProperties.LOCKED);
		if (locked == null) {
			throw new UnsupportedOperationException("Propriedade " + GraphProperties.LOCKED
					+ " não encontrada para Node: " + node.getProperty(GraphProperties.ID));
		}
		return locked;
	}

	@Override
	public void lock() {
		node.setProperty(GraphProperties.LOCKED, true);
	}

	@Override
	public void setD(Integer d) {
		node.setProperty(GraphProperties.D, d);
	}

	@Override
	public void setPartition(Integer partition) {
		if (partition < AbstractPartition.PART_1 || partition > AbstractPartition.PART_N) {
			throw new RuntimeException("Partição inválida: " + partition);
		}
		node.setProperty(GraphProperties.PARTITION, partition);
	}

	@Override
	public void resetPartition() {
		node.setProperty(GraphProperties.PARTITION, AbstractPartition.NO_PARTITION);
	}

	@Override
	public void setWeight(Integer weight) {
		node.setProperty(GraphProperties.PESO, weight);
	}

	@Override
	public void unlock() {
		node.setProperty(GraphProperties.LOCKED, false);
	}

	public Node getInnerNode() {
		return node;
	}

	@Override
	public long getInsideOf() {
		Long idInside = (Long) node.getProperty(GraphProperties.INSIDEOF);
		if (idInside == null) {
			throw new UnsupportedOperationException("Propriedade " + GraphProperties.INSIDEOF
					+ " não encontrada para Node: " + node.getProperty(GraphProperties.ID));
		}
		return idInside;
	}

	@Override
	public void setInsideOf(Long coarsedVertex) {
		node.setProperty(GraphProperties.INSIDEOF, coarsedVertex);
	}

	@Override
	public boolean hasInsideOf() {
		return node.hasProperty(GraphProperties.INSIDEOF);
	}

	@Override
	public boolean isCoarsed() {
		if (node.hasProperty(GraphProperties.COARSED)) {
			Boolean coarsed = (Boolean) node.getProperty(GraphProperties.COARSED);
			if (coarsed == null) {
				return false;
			}
			return coarsed;
		}
		return false;
	}

	@Override
	public void setCoarsed(boolean coarsed) {
		node.setProperty(GraphProperties.COARSED, coarsed);
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof NodeDB) {
			return node.equals(((NodeDB) obj).getInnerNode());
		}
		return false;
	}

	@Override
	public int hashCode() {
		return node.hashCode();
	}

	@Override
	public String toString() {
		return node.toString();
	}
}
